﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Entity.ModelConfiguration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MFTool;
using System.Reflection;
using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;
using System.Text.RegularExpressions;

namespace MF_Base.Model
{
    [Description("配置的表单")]
    public class FormConfig : Entity
    {
        public string MainTypeFullId { get; set; }
        public string UICode { get; set; }
        public string Name { get; set; }
        public string ModuleName { get; set; }
        public string TableName { get; set; }
        public string EntityFullName { get; set; }
        public string Script { get; set; }
        public string Remark { get; set; }

        public string GroupSetting { get; set; }
        public string CtrlSetting { get; set; }
        public string DataSourceSetting { get; set; }
        public string LayOutSetting { get; set; }

        public string EntityName
        {
            get
            {
                if (!string.IsNullOrEmpty(EntityFullName))
                {
                    int index = EntityFullName.LastIndexOf('.');
                    if (index == -1) index = 0;
                    return EntityFullName.Substring(index + 1);
                }
                return "";
            }
        }

        public bool IsDevelopTable()
        {
            return !string.IsNullOrEmpty(this.EntityFullName);
        }

        /// <summary>
        /// 更新表单控件配置
        /// </summary>
        /// <param name="dicList"></param>
        public void UpdateCtrl(List<Dictionary<string, object>> dicList)
        {
            CtrlSetting = dicList.ToJson();
        }
        /// <summary>
        /// 获取表单控件配置
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Dictionary<string, object>> GetCtrlAttrList()
        {
            var ctrlDicList = this.CtrlSetting.JsonToDictionaryList();

            Type type = null;
            if(IsDevelopTable())
            {
                type = ReflectionHelper.GetTypeBy(this.ModuleName, this.EntityFullName);
            }

            var resDicList = GetCtrlAttrList(this.ModuleName, this.TableName, ctrlDicList, type).ToList();
            int maxIndex = resDicList.Count();

            //子表Ctrl,数据库表结构不存在该列,但这个也作为表单定义的控件,因此这里要单独补入
            if(type != null)
            {
                var virtualPropertyDicList = GetVirtualPropertyDicList(type, ctrlDicList, maxIndex);
                resDicList.AddRange(virtualPropertyDicList);
            }

            return resDicList.OrderBy(a => { return Convert.ToInt32(a.GetValue("TmpOrderIndex")); });
        }
        
        /// <summary>
        /// 获取子表属性DicList
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="bContainForeignKeyField"></param>
        /// <returns></returns>
        public IEnumerable<Dictionary<string, object>> GetSubTablePropertyDicList(string fieldName)
        {
            Type type = ReflectionHelper.GetTypeBy(this.ModuleName, EntityFullName);
            var properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
            var singOne = properties.FirstOrDefault(a => a.Name == fieldName);
            singOne.CheckNotNull("未找到fieldName为{0}的字段".ReplaceArg(fieldName));
            var subTableType = singOne.PropertyType.GenericTypeArguments[0];
           
            var mainCtrlSetting = this.CtrlSetting.JsonToDictionaryList();
            var subTableDetail = mainCtrlSetting.FirstOrDefault(a => a.GetValue("FieldName") == fieldName);
            if (subTableDetail == null) subTableDetail = new Dictionary<string, object>();
            var detailDicList = subTableDetail.GetValue("Detail").JsonToDictionaryList();

            string moduleName = System.IO.Path.GetFileNameWithoutExtension(type.Module.Name);
            string tableName = subTableType.Name;
            return GetCtrlAttrList(moduleName, tableName, detailDicList, subTableType);
        }
        /// <summary>
        /// key tableName  value FieldName
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, string> GetPopupSelectorForeignTableFieldPairList()
        {
            var popupSelectors = CtrlSetting.JsonToDictionaryList().Where(a => a.GetValue("CtrlType") == ControlType.PopupSelector.ToString());
            Dictionary<string, string> res = new Dictionary<string, string>();
            foreach (var s in popupSelectors)
            {
                string detail = s.GetValue("Detail");
                if (!string.IsNullOrEmpty(detail))
                {
                    var detailDic = detail.JsonToDictionary();
                    res.SetValue(detailDic.GetValue("textName"), detailDic.GetValue("foreignTableFieldName"));
                }
            }
            return res;
        }
        public List<string> GetScriptFunctionList()
        {
            List<string> res = new List<string>();
            if(!string.IsNullOrEmpty(Script))
            {
                string RegexStr = @"function\s+([^\(]+)";
                Match mat = Regex.Match(Script, RegexStr);
                for (int i = 0; i < mat.Groups.Count; i++)
                {
                    res.Add(mat.Groups[i].Value);
                }
            }            
            return res;
        }
        #region 私有方法

        private IEnumerable<Dictionary<string, object>> GetCtrlAttrList(string moduleName, string tableName,
            List<Dictionary<string, object>> ctrlDicList, Type type = null)
        {
            //从数据库读取field
            var dbDtFieldList = SqlHelper.GetColumnNameWithType(moduleName, tableName);

            var properties = new List<PropertyInfo>();
            if(type != null)
            {
                properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).ToList();
                //基类的成员
                properties.AddRange(type.BaseType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance));
            }

            var resDicList = new List<Dictionary<string, object>>();

            int maxIndex = ctrlDicList.Count();
            foreach (var dbDtFieldDic in dbDtFieldList)
            {
                string dbDtField = dbDtFieldDic.GetValue("Name");
                var ctrlDic = ctrlDicList.SingleOrDefault(a => a.GetValue("FieldName") == dbDtField);

                if (ctrlDic == null)
                {
                    ctrlDic = new Dictionary<string, object>(); 
                    maxIndex++;
                    ctrlDic.SetValue("TmpOrderIndex", maxIndex);
                    ctrlDic.SetValue("FieldType", dbDtFieldDic.GetValue("TypeName"));
                    ctrlDic.SetValue("CtrlType", ControlType.HiddenInput.ToString());
                    ctrlDic.SetValue("FieldName", dbDtField);
                }
                else
                {     
                    ctrlDic.SetValue("TmpOrderIndex", ctrlDicList.IndexOf(ctrlDic));
                }                

                var dic = GetPropertyDic(properties, dbDtField);
                //如果包含类模型则要根据类的相关属性设置控件属性
                if (dic != null)
                {
                    foreach (var item in dic)
                    {
                        if ((item.Key == "ColumnName" || item.Key == "CtrlType" || item.Key == "Detail")
                            && !string.IsNullOrEmpty(ctrlDic.GetValue(item.Key)))
                            continue;

                        ctrlDic.SetValue(item.Key, item.Value);
                    }                    
                }

                resDicList.Add(ctrlDic);
            }

            //默认值处理
            foreach(var resDic in resDicList)
            {
                if (string.IsNullOrEmpty(resDic.GetValue("Enable")))
                {
                    resDic.SetValue("Enable", "是");
                }
                if (string.IsNullOrEmpty(resDic.GetValue("IsVisible")))
                {
                    resDic.SetValue("IsVisible", "是");
                }
            }
            return resDicList;
        }
        /// <summary>
        /// 获取数据库列对应的字段相关信息(ColumnName,FieldName,FieldType)
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private List<Dictionary<string, object>> GetPropertyDicList(Type type, bool bContainList = false)
        {
            List<Dictionary<string, object>> res = new List<Dictionary<string, object>>();
            var properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
            foreach (var property in properties)
            {
                Dictionary<string, object> dic = GetPropertyDic(properties, property.Name);
                if (dic != null)
                    res.Add(dic);
            }
            return res;
        }

        private Dictionary<string, object> GetPropertyDic(IEnumerable<PropertyInfo> properties, string fieldName)
        {
            var property = properties.FirstOrDefault(a => a.Name == fieldName);
            //必须可读可写
            if (property == null || !property.CanWrite || !property.CanRead)
                return null;

            Dictionary<string, object> dic = new Dictionary<string, object>();
            dic.SetValue("CtrlType", ControlType.HiddenInput.ToString());
            //非自定义类型
            var pType = property.PropertyType;
            if (!pType.IsPrimitive
                && pType != typeof(Decimal)
                 && pType != typeof(Decimal?)
                && pType != typeof(String)
                && pType != typeof(DateTime)
                && pType != typeof(DateTime?))
            {
                return null;
            }
            //外键字段
            else if (pType == typeof(String))
            {
                //获取子表的主外键字段名
                var v = (ForeignKeyAttribute[])property.GetCustomAttributes(typeof(ForeignKeyAttribute), false);
                if (v != null && v.Count() > 0)
                {
                    string tmpFieldName = v[0].Name;
                    var foreignTableProp = properties.SingleOrDefault(a => a.Name == tmpFieldName);
                    if (foreignTableProp != null)
                    {
                        dic.SetValue("Detail", "{{ \"ForeignTableName\":\"{0}\",\"connName\":\"{1}\"}}".ReplaceArg(foreignTableProp.PropertyType.Name, this.ModuleName));
                        dic.SetValue("CtrlType", ControlType.PopupSelector.ToString());
                    }
                }
            }

            object[] arr = property.GetCustomAttributes(typeof(DescriptionAttribute), true);

            dic.SetValue("ColumnName", "");
            if (arr.Length > 0 && !string.IsNullOrEmpty(((DescriptionAttribute)arr[0]).Description))
            {
                dic.SetValue("ColumnName", ((DescriptionAttribute)arr[0]).Description);
            }

            dic.SetValue("FieldName", property.Name);
            if (string.IsNullOrEmpty(dic.GetValue("ColumnName")))
            {
                dic.SetValue("ColumnName", property.Name);
            }
            var FieldType = ControlFieldTypeC.PropertyTypeStringToControlFieldType(property);
            dic.SetValue("FieldType", FieldType);
            dic.SetValue("IsDevelopField", "true");//开发级字段(Model字段)
            return dic;
        }

        private List<Dictionary<string, object>> GetVirtualPropertyDicList(Type type, List<Dictionary<string, object>> mainCtrlSetting, int maxIndex)
        {
            var properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance).ToList();

            var resDicList = new List<Dictionary<string, object>>();
            foreach(var property in properties)
            {
                //必须可读可写
                if (property == null || !property.CanWrite || !property.CanRead)
                    continue;

                //public virtual List<xxx>  XXXX;
                var pType = property.PropertyType;
                if (pType.Name != "List`1" || !property.GetAccessors().Any(a => a.IsVirtual))
                    continue;

                var v = property.GetCustomAttributes(typeof(JsonIgnoreAttribute), false);
                //如果带有JsonIgnore则不应该作为表单子表
                if (v.Length > 0)
                    continue;

                var dic = mainCtrlSetting.FirstOrDefault(a => a.GetValue("FieldName") == property.Name);
                if (dic == null)
                {
                    dic = new Dictionary<string, object>();
                    dic.SetValue("CtrlType", ControlType.SubDataGrid.ToString());
                    object[] arr = property.GetCustomAttributes(typeof(DescriptionAttribute), true);
                    dic.SetValue("ColumnName", "");
                    if (arr.Length > 0 && !string.IsNullOrEmpty(((DescriptionAttribute)arr[0]).Description))
                    {
                        dic.SetValue("ColumnName", ((DescriptionAttribute)arr[0]).Description);
                    }

                    dic.SetValue("FieldName", property.Name);
                    if (string.IsNullOrEmpty(dic.GetValue("ColumnName")))
                    {
                        dic.SetValue("ColumnName", property.Name);
                    }
                    var FieldType = ControlFieldTypeC.PropertyTypeStringToControlFieldType(property);
                    dic.SetValue("FieldType", FieldType);
                    dic.SetValue("IsDevelopField", "true");//开发级字段(Model字段)

                    maxIndex++;
                    dic.SetValue("TmpOrderIndex", maxIndex);
                }
                else
                {
                    dic.SetValue("TmpOrderIndex", mainCtrlSetting.IndexOf(dic));
                }

                //Detail
                var subTableType = pType.GenericTypeArguments[0];
                var detailDic = dic.GetValue("Detail").JsonToDictionary();
                var columnSetting = detailDic.GetValue("ColumnSetting").JsonToDictionaryList();

                string moduleName = System.IO.Path.GetFileNameWithoutExtension(type.Module.Name);
                string tableName = subTableType.Name;
                detailDic.SetValue("ColumnSetting", GetCtrlAttrList(moduleName, tableName, columnSetting, subTableType).ToJson());
                dic.SetValue("Detail", detailDic.ToJson());

                resDicList.Add(dic);
            }

            return resDicList;
        }
        #endregion
    }
}
